home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / cvs-1.8 / cvs-1 / cvs-1.8.1 / src / main.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-06  |  24.3 KB  |  815 lines

  1. /*
  2.  *    Copyright (c) 1992, Brian Berliner and Jeff Polk
  3.  *    Copyright (c) 1989-1992, Brian Berliner
  4.  *
  5.  *    You may distribute under the terms of the GNU General Public License
  6.  *    as specified in the README file that comes with the CVS 1.4 kit.
  7.  *
  8.  * This is the main C driver for the CVS system.
  9.  *
  10.  * Credit to Dick Grune, Vrije Universiteit, Amsterdam, for writing
  11.  * the shell-script CVS system that this is based on.
  12.  *
  13.  * Usage:
  14.  *    cvs [options] command [options] [files/modules...]
  15.  *
  16.  * Where "command" is composed of:
  17.  *        admin        RCS command
  18.  *        checkout    Check out a module/dir/file
  19.  *        export        Like checkout, but used for exporting sources
  20.  *        update        Brings work tree in sync with repository
  21.  *        commit        Checks files into the repository
  22.  *        diff        Runs diffs between revisions
  23.  *        log        Prints "rlog" information for files
  24.  *        login        Record user, host, repos, password
  25.  *        add        Adds an entry to the repository
  26.  *        remove        Removes an entry from the repository
  27.  *        status        Status info on the revisions
  28.  *        rdiff        "patch" format diff listing between releases
  29.  *        tag        Add/delete a symbolic tag to the RCS file
  30.  *        rtag        Add/delete a symbolic tag to the RCS file
  31.  *        import        Import sources into CVS, using vendor branches
  32.  *        release        Indicate that Module is no longer in use.
  33.  *        history        Display history of Users and Modules.
  34.  */
  35.  
  36. #include "cvs.h"
  37.  
  38. #ifdef HAVE_WINSOCK_H
  39. #include <winsock.h>
  40. #else
  41. extern int gethostname ();
  42. #endif
  43.  
  44. #if HAVE_KERBEROS
  45. #include <sys/socket.h>
  46. #include <netinet/in.h>
  47. #include <krb.h>
  48. #ifndef HAVE_KRB_GET_ERR_TEXT
  49. #define krb_get_err_text(status) krb_err_txt[status]
  50. #endif
  51. #endif
  52.  
  53. char *program_name;
  54. char *program_path;
  55. /*
  56.  * Initialize comamnd_name to "cvs" so that the first call to
  57.  * read_cvsrc tries to find global cvs options.
  58.  */
  59. char *command_name = "";
  60.  
  61. /*
  62.  * Since some systems don't define this...
  63.  */
  64. #ifndef MAXHOSTNAMELEN
  65. #define MAXHOSTNAMELEN  256
  66. #endif
  67.  
  68. char hostname[MAXHOSTNAMELEN];
  69.  
  70. #ifdef AUTH_CLIENT_SUPPORT
  71. int use_authenticating_server = FALSE;
  72. #endif /* AUTH_CLIENT_SUPPORT */
  73. int use_editor = TRUE;
  74. int use_cvsrc = TRUE;
  75. int cvswrite = !CVSREAD_DFLT;
  76. int really_quiet = FALSE;
  77. int quiet = FALSE;
  78. int trace = FALSE;
  79. int noexec = FALSE;
  80. int logoff = FALSE;
  81. mode_t cvsumask = UMASK_DFLT;
  82.  
  83. char *CurDir;
  84.  
  85. /*
  86.  * Defaults, for the environment variables that are not set
  87.  */
  88. char *Rcsbin = RCSBIN_DFLT;
  89. char *Editor = EDITOR_DFLT;
  90. char *CVSroot = CVSROOT_DFLT;
  91. /*
  92.  * The path found in CVS/Root must match $CVSROOT and/or 'cvs -d root'
  93.  */
  94. char *CVSADM_Root = CVSROOT_DFLT;
  95.  
  96. int add PROTO((int argc, char **argv));
  97. int admin PROTO((int argc, char **argv));
  98. int checkout PROTO((int argc, char **argv));
  99. int commit PROTO((int argc, char **argv));
  100. int diff PROTO((int argc, char **argv));
  101. int history PROTO((int argc, char **argv));
  102. int import PROTO((int argc, char **argv));
  103. int cvslog PROTO((int argc, char **argv));
  104. #ifdef AUTH_CLIENT_SUPPORT
  105. int login PROTO((int argc, char **argv));
  106. #endif /* AUTH_CLIENT_SUPPORT */
  107. int patch PROTO((int argc, char **argv));
  108. int release PROTO((int argc, char **argv));
  109. int cvsremove PROTO((int argc, char **argv));
  110. int rtag PROTO((int argc, char **argv));
  111. int status PROTO((int argc, char **argv));
  112. int tag PROTO((int argc, char **argv));
  113. int update PROTO((int argc, char **argv));
  114.  
  115. const struct cmd
  116. {
  117.     char *fullname;        /* Full name of the function (e.g. "commit") */
  118.     char *nick1;        /* alternate name (e.g. "ci") */
  119.     char *nick2;        /* another alternate names (e.g. "ci") */
  120.     int (*func) ();        /* Function takes (argc, argv) arguments. */
  121. #ifdef CLIENT_SUPPORT
  122.     int (*client_func) ();    /* Function to do it via the protocol.  */
  123. #endif
  124. } cmds[] =
  125.  
  126. {
  127. #ifdef CLIENT_SUPPORT
  128. #define CMD_ENTRY(n1, n2, n3, f1, f2) { n1, n2, n3, f1, f2 }
  129. #else
  130. #define CMD_ENTRY(n1, n2, n3, f1, f2) { n1, n2, n3, f1 }
  131. #endif
  132.  
  133.     CMD_ENTRY("add",      "ad",    "new",     add,       client_add),
  134.     CMD_ENTRY("admin",    "adm",   "rcs",     admin,     client_admin),
  135.     CMD_ENTRY("annotate", NULL,    NULL,      annotate,  client_annotate),
  136.     CMD_ENTRY("checkout", "co",    "get",     checkout,  client_checkout),
  137.     CMD_ENTRY("commit",   "ci",    "com",     commit,    client_commit),
  138.     CMD_ENTRY("diff",     "di",    "dif",     diff,      client_diff),
  139.     CMD_ENTRY("edit",     "edit",  "edit",    edit,      client_edit),
  140.     CMD_ENTRY("editors",  "editors","editors",editors,   client_editors),
  141.     CMD_ENTRY("export",   "exp",   "ex",      checkout,  client_export),
  142.     CMD_ENTRY("history",  "hi",    "his",     history,   client_history),
  143.     CMD_ENTRY("import",   "im",    "imp",     import,    client_import),
  144.     CMD_ENTRY("init",     NULL,    NULL,      init,      client_init),
  145.     CMD_ENTRY("log",      "lo",    "rlog",    cvslog,    client_log),
  146. #ifdef AUTH_CLIENT_SUPPORT
  147.     CMD_ENTRY("login",    "logon", "lgn",     login,     login),
  148. #endif /* AUTH_CLIENT_SUPPORT */
  149.     CMD_ENTRY("rdiff",    "patch", "pa",      patch,     client_rdiff),
  150.     CMD_ENTRY("release",  "re",    "rel",     release,   client_release),
  151.     CMD_ENTRY("remove",   "rm",    "delete",  cvsremove, client_remove),
  152.     CMD_ENTRY("status",   "st",    "stat",    status,    client_status),
  153.     CMD_ENTRY("rtag",     "rt",    "rfreeze", rtag,      client_rtag),
  154.     CMD_ENTRY("tag",      "ta",    "freeze",  tag,       client_tag),
  155.     CMD_ENTRY("unedit",   "unedit","unedit",  unedit,    client_unedit),
  156.     CMD_ENTRY("update",   "up",    "upd",     update,    client_update),
  157.     CMD_ENTRY("watch",    "watch", "watch",   watch,     client_watch),
  158.     CMD_ENTRY("watchers", "watchers","watchers",watchers,client_watchers),
  159. #ifdef SERVER_SUPPORT
  160.     /*
  161.      * The client_func is also server because we might have picked up a
  162.      * CVSROOT environment variable containing a colon.  The client will send
  163.      * the real root later.
  164.      */
  165.     CMD_ENTRY("server",   "server", "server", server,    server),
  166. #endif
  167.     CMD_ENTRY(NULL, NULL, NULL, NULL, NULL),
  168.  
  169. #undef CMD_ENTRY
  170. };
  171.  
  172. static const char *const usg[] =
  173. {
  174.     "Usage: %s [cvs-options] command [command-options] [files...]\n",
  175.     "    Where 'cvs-options' are:\n",
  176.     "        -H           Displays Usage information for command\n",
  177.     "        -Q           Cause CVS to be really quiet.\n",
  178.     "        -q           Cause CVS to be somewhat quiet.\n",
  179.     "        -r           Make checked-out files read-only\n",
  180.     "        -w           Make checked-out files read-write (default)\n",
  181.     "        -l           Turn History logging off\n",
  182.     "        -n           Do not execute anything that will change the disk\n",
  183.     "        -t           Show trace of program execution -- Try with -n\n",
  184.     "        -v           CVS version and copyright\n",
  185.     "        -b bindir    Find RCS programs in 'bindir'\n",
  186.     "        -e editor    Use 'editor' for editing log information\n",
  187.     "        -d CVS_root  Overrides $CVSROOT as the root of the CVS tree\n",
  188.     "        -f           Do not use the ~/.cvsrc file\n",
  189. #ifdef CLIENT_SUPPORT
  190.     "        -z #         Use 'gzip -#' for net traffic if possible.\n",
  191. #endif
  192.     "        -s VAR=VAL   Set CVS user variable.\n",
  193.     "\n",
  194.     "    and where 'command' is: add, admin, etc. (use the --help-commands\n",
  195.     "    option for a list of commands)\n",
  196.     NULL,
  197. };
  198.  
  199. static const char *const cmd_usage[] =
  200. {
  201.     "CVS commands are:\n",
  202.     "        add          Adds a new file/directory to the repository\n",
  203.     "        admin        Administration front end for rcs\n",
  204.     "        annotate     Show revision where each line was modified\n",
  205.     "        checkout     Checkout sources for editing\n",
  206.     "        commit       Checks files into the repository\n",
  207.     "        diff         Runs diffs between revisions\n",
  208.     "        edit         Get ready to edit a watched file\n",
  209.     "        editors      See who is editing a watched file\n",
  210.     "        history      Shows status of files and users\n",
  211.     "        import       Import sources into CVS, using vendor branches\n",
  212.     "        export       Export sources from CVS, similar to checkout\n",
  213.     "        log          Prints out 'rlog' information for files\n",
  214. #ifdef AUTH_CLIENT_SUPPORT
  215.     "        login        Prompt for password for authenticating server.\n",
  216. #endif /* AUTH_CLIENT_SUPPORT */
  217.     "        rdiff        'patch' format diffs between releases\n",
  218.     "        release      Indicate that a Module is no longer in use\n",
  219.     "        remove       Removes an entry from the repository\n",
  220.     "        status       Status info on the revisions\n",
  221.     "        tag          Add a symbolic tag to checked out version of RCS file\n",
  222.     "        unedit       Undo an edit command\n",
  223.     "        rtag         Add a symbolic tag to the RCS file\n",
  224.     "        update       Brings work tree in sync with repository\n",
  225.     "        watch        Set watches\n",
  226.     "        watchers     See who is watching a file\n",
  227.     NULL,
  228. };
  229.  
  230. static RETSIGTYPE
  231. main_cleanup ()
  232. {
  233.     exit (EXIT_FAILURE);
  234. }
  235.  
  236. static void
  237. error_cleanup PROTO((void))
  238. {
  239.     Lock_Cleanup();
  240. #ifdef SERVER_SUPPORT
  241.     if (server_active)
  242.     server_cleanup (0);
  243. #endif
  244. }
  245.  
  246. int
  247. main (argc, argv)
  248.     int argc;
  249.     char **argv;
  250. {
  251.     extern char *version_string;
  252.     extern char *config_string;
  253.     char *cp, *end;
  254.     const struct cmd *cm;
  255.     int c, err = 0;
  256.     static int help = FALSE;
  257.     static int version_flag = FALSE;
  258.     static int help_commands = FALSE;
  259.     int rcsbin_update_env, cvs_update_env = 0;
  260.     static struct option long_options[] =
  261.       {
  262.         {"help", 0, &help, TRUE},
  263.         {"version", 0, &version_flag, TRUE},
  264.     {"help-commands", 0, &help_commands, TRUE},
  265.         {0, 0, 0, 0}
  266.       };
  267.     /* `getopt_long' stores the option index here, but right now we
  268.         don't use it. */
  269.     int option_index = 0;
  270.  
  271.     error_set_cleanup (error_cleanup);
  272.  
  273. /* The socket subsystems on NT and OS2 must be initialized before use */
  274. #ifdef INITIALIZE_SOCKET_SUBSYSTEM
  275.         INITIALIZE_SOCKET_SUBSYSTEM();
  276. #endif /* INITIALIZE_SOCKET_SUBSYSTEM */
  277.  
  278.     /*
  279.      * Just save the last component of the path for error messages
  280.      */
  281.     program_path = xstrdup (argv[0]);
  282.     program_name = last_component (argv[0]);
  283.  
  284.     CurDir = xmalloc (PATH_MAX);
  285. #ifndef SERVER_SUPPORT
  286.     if (!getwd (CurDir))
  287.     error (1, 0, "cannot get working directory: %s", CurDir);
  288. #endif
  289.  
  290.     /*
  291.      * Query the environment variables up-front, so that
  292.      * they can be overridden by command line arguments
  293.      */
  294.     rcsbin_update_env = *Rcsbin;    /* RCSBIN_DFLT must be set */
  295.     cvs_update_env = 0;
  296.     if ((cp = getenv (RCSBIN_ENV)) != NULL)
  297.     {
  298.     Rcsbin = cp;
  299.     rcsbin_update_env = 0;        /* it's already there */
  300.     }
  301.     if ((cp = getenv (EDITOR1_ENV)) != NULL)
  302.      Editor = cp;
  303.     else if ((cp = getenv (EDITOR2_ENV)) != NULL)
  304.     Editor = cp;
  305.     else if ((cp = getenv (EDITOR3_ENV)) != NULL)
  306.     Editor = cp;
  307.     if ((cp = getenv (CVSROOT_ENV)) != NULL)
  308.     {
  309.     CVSroot = cp;
  310.     cvs_update_env = 0;        /* it's already there */
  311.     }
  312.     if (getenv (CVSREAD_ENV) != NULL)
  313.     cvswrite = FALSE;
  314.     if ((cp = getenv (CVSUMASK_ENV)) != NULL)
  315.     {
  316.     /* FIXME: Should be accepting symbolic as well as numeric mask.  */
  317.     cvsumask = strtol (cp, &end, 8) & 0777;
  318.     if (*end != '\0')
  319.         error (1, errno, "invalid umask value in %s (%s)",
  320.         CVSUMASK_ENV, cp);
  321.     }
  322.  
  323.     /* This has the effect of setting getopt's ordering to REQUIRE_ORDER,
  324.        which is what we need to distinguish between global options and
  325.        command options.  FIXME: It would appear to be possible to do this
  326.        much less kludgily by passing "+" as the first character to the
  327.        option string we pass to getopt_long.  */
  328.     optind = 1;
  329.  
  330.  
  331.     /* We have to parse the options twice because else there is no
  332.        chance to avoid reading the global options from ".cvsrc".  Set
  333.        opterr to 0 for avoiding error messages about invalid options.
  334.        */
  335.     opterr = 0;
  336.  
  337.     while ((c = getopt_long
  338.             (argc, argv, "f", NULL, NULL))
  339.            != EOF)
  340.       {
  341.     if (c == 'f')
  342.         use_cvsrc = FALSE;
  343.       }
  344.     
  345.     /*
  346.      * Scan cvsrc file for global options.
  347.      */
  348.     if (use_cvsrc)
  349.     read_cvsrc (&argc, &argv, "cvs");
  350.  
  351.     optind = 1;
  352.     opterr = 1;
  353.  
  354.     while ((c = getopt_long
  355.             (argc, argv, "Qqrwtnlvb:e:d:Hfz:s:", long_options, &option_index))
  356.            != EOF)
  357.       {
  358.     switch (c)
  359.           {
  360.             case 0:
  361.                 /* getopt_long took care of setting the flag. */ 
  362.                 break;
  363.         case 'Q':
  364.         really_quiet = TRUE;
  365.         /* FALL THROUGH */
  366.         case 'q':
  367.         quiet = TRUE;
  368.         break;
  369.         case 'r':
  370.         cvswrite = FALSE;
  371.         break;
  372.         case 'w':
  373.         cvswrite = TRUE;
  374.         break;
  375.         case 't':
  376.         trace = TRUE;
  377.         break;
  378.         case 'n':
  379.         noexec = TRUE;
  380.         case 'l':            /* Fall through */
  381.         logoff = TRUE;
  382.         break;
  383.         case 'v':
  384.                 version_flag = TRUE;
  385.         break;
  386.         case 'b':
  387.         Rcsbin = optarg;
  388.         rcsbin_update_env = 1;    /* need to update environment */
  389.         break;
  390.         case 'e':
  391.         Editor = optarg;
  392.         break;
  393.         case 'd':
  394.         CVSroot = optarg;
  395.         cvs_update_env = 1;    /* need to update environment */
  396.         break;
  397.         case 'H':
  398.         use_cvsrc = FALSE;      /* this ensure that cvs -H works */
  399.         help = TRUE;
  400.         break;
  401.             case 'f':
  402.         use_cvsrc = FALSE;
  403.         break;
  404.         case 'z':
  405. #ifdef CLIENT_SUPPORT
  406.         gzip_level = atoi (optarg);
  407.         if (gzip_level <= 0 || gzip_level > 9)
  408.           error (1, 0,
  409.              "gzip compression level must be between 1 and 9");
  410. #endif
  411.         /* If no CLIENT_SUPPORT, we just silently ignore the gzip
  412.            level, so that users can have it in their .cvsrc and not
  413.            cause any trouble.  */
  414.         break;
  415.         case 's':
  416.         variable_set (optarg);
  417.         break;
  418.         case '?':
  419.         default:
  420.                 usage (usg);
  421.     }
  422.     }
  423.  
  424.     if (version_flag == TRUE)
  425.     {
  426.         (void) fputs (version_string, stdout);
  427.         (void) fputs (config_string, stdout);
  428.         (void) fputs ("\n", stdout);
  429.         (void) fputs ("Copyright (c) 1993-1994 Brian Berliner\n", stdout);
  430.         (void) fputs ("Copyright (c) 1993-1994 david d `zoo' zuhn\n", stdout);
  431.         (void) fputs ("Copyright (c) 1992, Brian Berliner and Jeff Polk\n", stdout);
  432.         (void) fputs ("Copyright (c) 1989-1992, Brian Berliner\n", stdout);
  433.         (void) fputs ("\n", stdout);
  434.         (void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout);
  435.         (void) fputs ("a copy of which can be found with the CVS distribution kit.\n", stdout);
  436.         exit (0);
  437.     }
  438.     else if (help_commands)
  439.     usage (cmd_usage);
  440.  
  441.     argc -= optind;
  442.     argv += optind;
  443.     if (argc < 1)
  444.     usage (usg);
  445.  
  446. #ifdef HAVE_KERBEROS
  447.     /* If we are invoked with a single argument "kserver", then we are
  448.        running as Kerberos server as root.  Do the authentication as
  449.        the very first thing, to minimize the amount of time we are
  450.        running as root.  */
  451.     if (strcmp (argv[0], "kserver") == 0)
  452.     {
  453.     int status;
  454.     char instance[INST_SZ];
  455.     struct sockaddr_in peer;
  456.     struct sockaddr_in laddr;
  457.     int len;
  458.     KTEXT_ST ticket;
  459.     AUTH_DAT auth;
  460.     char version[KRB_SENDAUTH_VLEN];
  461.     Key_schedule sched;
  462.     char user[ANAME_SZ];
  463.     struct passwd *pw;
  464.  
  465.     strcpy (instance, "*");
  466.     len = sizeof peer;
  467.     if (getpeername (STDIN_FILENO, (struct sockaddr *) &peer, &len) < 0
  468.         || getsockname (STDIN_FILENO, (struct sockaddr *) &laddr,
  469.                 &len) < 0)
  470.     {
  471.         printf ("E Fatal error, aborting.\n\
  472. error %s getpeername or getsockname failed\n", strerror (errno));
  473.         exit (EXIT_FAILURE);
  474.     }
  475.  
  476.     status = krb_recvauth (KOPT_DO_MUTUAL, STDIN_FILENO, &ticket, "rcmd",
  477.                    instance, &peer, &laddr, &auth, "", sched,
  478.                    version);
  479.     if (status != KSUCCESS)
  480.     {
  481.         printf ("E Fatal error, aborting.\n\
  482. error 0 kerberos: %s\n", krb_get_err_text(status));
  483.         exit (EXIT_FAILURE);
  484.     }
  485.  
  486.     /* Get the local name.  */
  487.     status = krb_kntoln (&auth, user);
  488.     if (status != KSUCCESS)
  489.     {
  490.         printf ("E Fatal error, aborting.\n\
  491. error 0 kerberos: can't get local name: %s\n", krb_get_err_text(status));
  492.         exit (EXIT_FAILURE);
  493.     }
  494.  
  495.     pw = getpwnam (user);
  496.     if (pw == NULL)
  497.     {
  498.         printf ("E Fatal error, aborting.\n\
  499. error 0 %s: no such user\n", user);
  500.         exit (EXIT_FAILURE);
  501.     }
  502.  
  503.     initgroups (pw->pw_name, pw->pw_gid);
  504.     setgid (pw->pw_gid);
  505.     setuid (pw->pw_uid);
  506.     /* Inhibit access by randoms.  Don't want people randomly
  507.        changing our temporary tree before we check things in.  */
  508.     umask (077);
  509.  
  510. #if HAVE_PUTENV
  511.     /* Set LOGNAME and USER in the environment, in case they are
  512.            already set to something else.  */
  513.     {
  514.         char *env;
  515.  
  516.         env = xmalloc (sizeof "LOGNAME=" + strlen (user));
  517.         (void) sprintf (env, "LOGNAME=%s", user);
  518.         (void) putenv (env);
  519.  
  520.         env = xmalloc (sizeof "USER=" + strlen (user));
  521.         (void) sprintf (env, "USER=%s", user);
  522.         (void) putenv (env);
  523.     }
  524. #endif
  525.  
  526.     /* Pretend we were invoked as a plain server.  */
  527.     argv[0] = "server";
  528.     }
  529. #endif /* HAVE_KERBEROS */
  530.  
  531.  
  532. #if defined(AUTH_SERVER_SUPPORT) && defined(SERVER_SUPPORT)
  533.     if (strcmp (argv[0], "pserver") == 0)
  534.     {
  535.       /* Gets username and password from client, authenticates, then
  536.          switches to run as that user and sends an ACK back to the
  537.          client. */
  538.       authenticate_connection ();
  539.       
  540.       /* Pretend we were invoked as a plain server.  */
  541.       argv[0] = "server";
  542.     }
  543. #endif /* AUTH_SERVER_SUPPORT && SERVER_SUPPORT */
  544.  
  545.  
  546.     /*
  547.      * See if we are able to find a 'better' value for CVSroot in the
  548.      * CVSADM_ROOT directory.
  549.      */
  550. #ifdef SERVER_SUPPORT
  551.     if (strcmp (argv[0], "server") == 0 && CVSroot == NULL)
  552.         CVSADM_Root = NULL;
  553.     else
  554.         CVSADM_Root = Name_Root((char *) NULL, (char *) NULL);
  555. #else /* No SERVER_SUPPORT */
  556.     CVSADM_Root = Name_Root((char *) NULL, (char *) NULL);
  557. #endif /* No SERVER_SUPPORT */
  558.     if (CVSADM_Root != NULL)
  559.     {
  560.         if (CVSroot == NULL || !cvs_update_env)
  561.         {
  562.         CVSroot = CVSADM_Root;
  563.         cvs_update_env = 1;    /* need to update environment */
  564.         }
  565. #ifdef CLIENT_SUPPORT
  566.         else if (!getenv ("CVS_IGNORE_REMOTE_ROOT"))
  567. #else /* ! CLIENT_SUPPORT */
  568.         else
  569. #endif /* CLIENT_SUPPORT */
  570.         {
  571.             /*
  572.          * Now for the hard part, compare the two directories. If they
  573.          * are not identical, then abort this command.
  574.          */
  575.             if ((fncmp (CVSroot, CVSADM_Root) != 0) &&
  576.         !same_directories(CVSroot, CVSADM_Root))
  577.         {
  578.               error (0, 0, "%s value for CVS Root found in %s",
  579.                      CVSADM_Root, CVSADM_ROOT);
  580.               error (0, 0, "does not match command line -d %s setting",
  581.                      CVSroot);
  582.               error (1, 0,
  583.                       "you may wish to try the cvs command again without the -d option ");
  584.         }
  585.         }
  586.     }
  587.  
  588.     /* CVSroot may need fixing up, if an access-method was specified,
  589.      * but not a user.  Later code assumes that if CVSroot contains an
  590.      * access-method, then it also has a user.  We print a warning and
  591.      * die if we can't guarantee that.
  592.      */
  593.     if (CVSroot
  594.         && *CVSroot
  595.         && (CVSroot[0] == ':')
  596.         && (strchr (CVSroot, '@') == NULL))
  597.       {
  598.         error (1, 0,
  599.                "must also give a username if specifying access method");
  600.       }
  601.  
  602.     /*
  603.      * Specifying just the '-H' flag to the sub-command causes a Usage
  604.      * message to be displayed.
  605.      */
  606.     command_name = cp = argv[0];
  607.     if (help == TRUE || (argc > 1 && strcmp (argv[1], "-H") == 0))
  608.     argc = -1;
  609.     else
  610.     {
  611.     /*
  612.      * Check to see if we can write into the history file.  If not,
  613.      * we assume that we can't work in the repository.
  614.      * BUT, only if the history file exists.
  615.      */
  616. #ifdef SERVER_SUPPORT
  617.         if (strcmp (command_name, "server") != 0 || CVSroot != NULL)
  618. #endif
  619.     {
  620.         char path[PATH_MAX];
  621.         int save_errno;
  622.  
  623.         if (!CVSroot || !*CVSroot)
  624.         error (1, 0, "You don't have a %s environment variable",
  625.                CVSROOT_ENV);
  626.         (void) sprintf (path, "%s/%s", CVSroot, CVSROOTADM);
  627.         if (!isaccessible (path, R_OK | X_OK))
  628.         {
  629.         save_errno = errno;
  630.         /* If this is "cvs init", the root need not exist yet.  */
  631.         if (strcmp (command_name, "init") != 0
  632. #ifdef CLIENT_SUPPORT
  633.             /* If we are a remote client, the root need not exist
  634.                on the client machine (FIXME: we should also skip
  635.                the check for CVSROOTADM_HISTORY being writable;
  636.                it shouldn't matter if there is a read-only file
  637.                which happens to have the same name on the client
  638.                machine).  */
  639.             && strchr (CVSroot, ':') == NULL)
  640. #endif
  641.         {
  642.         error (0, 0,
  643.             "Sorry, you don't have sufficient access to %s", CVSroot);
  644.         error (1, save_errno, "%s", path);
  645.         }
  646.         }
  647.         (void) strcat (path, "/");
  648.         (void) strcat (path, CVSROOTADM_HISTORY);
  649.         if (isfile (path) && !isaccessible (path, R_OK | W_OK))
  650.         {
  651.         save_errno = errno;
  652.         error (0, 0,
  653.          "Sorry, you don't have read/write access to the history file");
  654.         error (1, save_errno, "%s", path);
  655.         }
  656.     }
  657.     }
  658.  
  659. #ifdef SERVER_SUPPORT
  660.     if (strcmp (command_name, "server") == 0)
  661.     /* This is only used for writing into the history file.  Might
  662.        be nice to have hostname and/or remote path, on the other hand
  663.        I'm not sure whether it is worth the trouble.  */
  664.     strcpy (CurDir, "<remote>");
  665.     else if (!getwd (CurDir))
  666.     error (1, 0, "cannot get working directory: %s", CurDir);
  667. #endif
  668.  
  669. #ifdef HAVE_PUTENV
  670.     /* Now, see if we should update the environment with the Rcsbin value */
  671.     if (cvs_update_env)
  672.     {
  673.     char *env;
  674.  
  675.     env = xmalloc (strlen (CVSROOT_ENV) + strlen (CVSroot) + 1 + 1);
  676.     (void) sprintf (env, "%s=%s", CVSROOT_ENV, CVSroot);
  677.     (void) putenv (env);
  678.     /* do not free env, as putenv has control of it */
  679.     }
  680.     if (rcsbin_update_env)
  681.     {
  682.     char *env;
  683.  
  684.     env = xmalloc (strlen (RCSBIN_ENV) + strlen (Rcsbin) + 1 + 1);
  685.     (void) sprintf (env, "%s=%s", RCSBIN_ENV, Rcsbin);
  686.     (void) putenv (env);
  687.     /* do not free env, as putenv has control of it */
  688.     }
  689. #endif
  690.  
  691.     /*
  692.      * If Rcsbin is set to something, make sure it is terminated with
  693.      * a slash character.  If not, add one.
  694.      */
  695.     if (*Rcsbin)
  696.     {
  697.     int len = strlen (Rcsbin);
  698.     char *rcsbin;
  699.  
  700.     if (Rcsbin[len - 1] != '/')
  701.     {
  702.         rcsbin = Rcsbin;
  703.         Rcsbin = xmalloc (len + 2);    /* one for '/', one for NULL */
  704.         (void) strcpy (Rcsbin, rcsbin);
  705.         (void) strcat (Rcsbin, "/");
  706.     }
  707.     }
  708.  
  709.     for (cm = cmds; cm->fullname; cm++)
  710.     {
  711.     if (cm->nick1 && !strcmp (cp, cm->nick1))
  712.         break;
  713.     if (cm->nick2 && !strcmp (cp, cm->nick2))
  714.         break;
  715.     if (!strcmp (cp, cm->fullname))
  716.         break;
  717.     }
  718.  
  719.     if (!cm->fullname)
  720.     usage (usg);            /* no match */
  721.     else
  722.     {
  723.     command_name = cm->fullname;    /* Global pointer for later use */
  724.  
  725.     /* make sure we clean up on error */
  726. #ifdef SIGHUP
  727.     (void) SIG_register (SIGHUP, main_cleanup);
  728.     (void) SIG_register (SIGHUP, Lock_Cleanup);
  729. #endif
  730. #ifdef SIGINT
  731.     (void) SIG_register (SIGINT, main_cleanup);
  732.     (void) SIG_register (SIGINT, Lock_Cleanup);
  733. #endif
  734. #ifdef SIGQUIT
  735.     (void) SIG_register (SIGQUIT, main_cleanup);
  736.     (void) SIG_register (SIGQUIT, Lock_Cleanup);
  737. #endif
  738. #ifdef SIGPIPE
  739.     (void) SIG_register (SIGPIPE, main_cleanup);
  740.     (void) SIG_register (SIGPIPE, Lock_Cleanup);
  741. #endif
  742. #ifdef SIGTERM
  743.     (void) SIG_register (SIGTERM, main_cleanup);
  744.     (void) SIG_register (SIGTERM, Lock_Cleanup);
  745. #endif
  746.  
  747.     gethostname(hostname, sizeof (hostname));
  748.  
  749. #ifdef HAVE_SETVBUF
  750.     /*
  751.      * Make stdout line buffered, so 'tail -f' can monitor progress.
  752.      * Patch creates too much output to monitor and it runs slowly.
  753.      */
  754.     if (strcmp (cm->fullname, "patch"))
  755.         (void) setvbuf (stdout, (char *) NULL, _IOLBF, 0);
  756. #endif
  757.  
  758.     if (use_cvsrc)
  759.       read_cvsrc (&argc, &argv, command_name);
  760.  
  761. #ifdef CLIENT_SUPPORT
  762.     /* If cvsroot contains a colon, try to do it via the protocol.  */
  763.         {
  764.         char *p = CVSroot == NULL ? NULL : strchr (CVSroot, ':');
  765.         if (p)
  766.         err = (*(cm->client_func)) (argc, argv);
  767.         else
  768.         err = (*(cm->func)) (argc, argv);
  769.     }
  770. #else /* No CLIENT_SUPPORT */
  771.     err = (*(cm->func)) (argc, argv);
  772.  
  773. #endif /* No CLIENT_SUPPORT */
  774.     }
  775.     Lock_Cleanup ();
  776.     if (err)
  777.     return (EXIT_FAILURE);
  778.     return 0;
  779. }
  780.  
  781. char *
  782. Make_Date (rawdate)
  783.     char *rawdate;
  784. {
  785.     struct tm *ftm;
  786.     time_t unixtime;
  787.     char date[256];            /* XXX bigger than we'll ever need? */
  788.     char *ret;
  789.  
  790.     unixtime = get_date (rawdate, (struct timeb *) NULL);
  791.     if (unixtime == (time_t) - 1)
  792.     error (1, 0, "Can't parse date/time: %s", rawdate);
  793. #ifdef HAVE_RCS5
  794.     ftm = gmtime (&unixtime);
  795. #else
  796.     ftm = localtime (&unixtime);
  797. #endif
  798.     (void) sprintf (date, DATEFORM,
  799.             ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
  800.             ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
  801.             ftm->tm_min, ftm->tm_sec);
  802.     ret = xstrdup (date);
  803.     return (ret);
  804. }
  805.  
  806. void
  807. usage (cpp)
  808.     register const char *const *cpp;
  809. {
  810.     (void) fprintf (stderr, *cpp++, program_name, command_name);
  811.     for (; *cpp; cpp++)
  812.     (void) fprintf (stderr, *cpp);
  813.     exit (EXIT_FAILURE);
  814. }
  815.